﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using Workbench.Models;
using Workbench.Operations;
using Workbench.Shared;

namespace Workbench.Presentation
{
    public class POSTaggerViewModel : ProcessWordsViewViewModelBase
    {
        public POSTaggerViewModel()
            : base("POS Tagging")
        {
            WordsListViewModel.PropertyChanged += WordsListViewModel_PropertyChanged;

            setPOSTypeCommand = new _SetPOSTypeCommand(this);
            unSetPOSTypeCommand = new _UnSetPOSTypeCommand(this);
        }

        public override void Load()
        {
            var rootDirectory = ProjectOperations.GetRootDirectory();
            FileSelectViewModel.ShowFiles(rootDirectory);

            var posTypesHierarchy = ProjectOperations.GetPOSTypes();
            POSTypes.AddRange(flatten(posTypesHierarchy, 0));
        }

        public override void Unload()
        {
            this.POSTypes.Clear();
            this.CurrentBaseForm = null;
            this.AssignedPOSTypes.Clear();
            this.FileSelectViewModel.Unload();
            this.SentenceViewModel.Unload();
            this.WordsListViewModel.Unload();
        }

        #region Properties

        private ObservableCollection<FlattenPOSTypeViewModel> posTypes = new ObservableCollection<FlattenPOSTypeViewModel>();
        public ObservableCollection<FlattenPOSTypeViewModel> POSTypes
        {
            get { return posTypes; }
        }

        private FlattenPOSTypeViewModel selectedPOSType;
        public FlattenPOSTypeViewModel SelectedPOSType
        {
            get { return selectedPOSType; }
            set { selectedPOSType = value; RaisePropertyChanged(); }
        }

        private FlattenPOSTypeViewModel selectedAssignedPOSType;
        public FlattenPOSTypeViewModel SelectedAssignedPOSType
        {
            get { return selectedAssignedPOSType; }
            set { selectedAssignedPOSType = value; RaisePropertyChanged(); }
        }


        private string currentBaseForm;
        public string CurrentBaseForm
        {
            get { return currentBaseForm; }
            set { currentBaseForm = value; RaisePropertyChanged(); }
        }

        private ObservableCollection<FlattenPOSTypeViewModel> assignedPOSTypes = new ObservableCollection<FlattenPOSTypeViewModel>();
        public ObservableCollection<FlattenPOSTypeViewModel> AssignedPOSTypes
        {
            get { return assignedPOSTypes; }
        }

        #endregion Properties

        #region Event Handlers

        void WordsListViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (string.Compare(e.PropertyName, "LastSelectedWord", false) == 0)
            {
                var vm = sender as ProcessWordsListViewModel<WordViewModelBase>;
                if (vm.LastSelectedWord == null) return;
                ShowBaseForm(vm.LastSelectedWord.BaseForm);
                ShowPOSType(vm.LastSelectedWord as POSTaggerWordViewModel);
            }
        }

        #endregion Event Handlers

        #region Commands

        #region SetPOSTypeCommand

        readonly _SetPOSTypeCommand setPOSTypeCommand;
        public ICommand SetPOSTypeCommand
        {
            get { return setPOSTypeCommand; }
        }

        private class _SetPOSTypeCommand : ICommand
        {
            protected POSTaggerViewModel viewModel;

            public _SetPOSTypeCommand(POSTaggerViewModel viewModel)
            {
                this.viewModel = viewModel;

                viewModel.PropertyChanged += viewModel_PropertyChanged;
                viewModel.WordsListViewModel.SelectedWords.CollectionChanged += SelectedWords_CollectionChanged;
            }

            void viewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                if (e.PropertyName == "SelectedPOSType")
                    RaiseCanExecuteChanged();
            }

            private void SelectedWords_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                RaiseCanExecuteChanged();
            }

            private void RaiseCanExecuteChanged()
            {
                if (CanExecuteChanged != null)
                    CanExecuteChanged(null, EventArgs.Empty);
            }

            public bool CanExecute(object parameter)
            {
                if (viewModel.WordsListViewModel.SelectedWords.Count == 0)
                    return false;

                if (viewModel.SelectedPOSType == null)
                    return false;

                if (viewModel.WordsListViewModel.LastSelectedWord.DataModel.POSTypes.Contains(viewModel.SelectedPOSType.DataModel))
                    return false;

                return true;
            }

            public void Execute(object parameter)
            {
                switch (viewModel.OperationContext)
                {
                    case OperationContext.Selection:
                        POSTaggerOperations.AddPosTypeToWords(viewModel.SelectedPOSType.DataModel,
                                viewModel.WordsListViewModel.SelectedWords.Select(x => x.DataModel));
                        break;
                    case OperationContext.CurrentFile:
                        {
                            var uniqueWordId = viewModel.WordsListViewModel.LastSelectedWord.UniqueWordId;
                            var fileId = viewModel.FileSelectViewModel.SelectedFile.RowId;
                            var posTypeId = viewModel.selectedPOSType.DataModel.RowId;
                            POSTaggerOperations.AddPosTypeToWordInFile(posTypeId, uniqueWordId, fileId);
                            var words = viewModel.WordsListViewModel
                                                .AvailableWords
                                                .Where(x => x.UniqueWordId == uniqueWordId)
                                                .Cast<POSTaggerWordViewModel>();
                            foreach (var word in words)
                            {
                                word.DataModel.POSTypes.Add(viewModel.selectedPOSType.DataModel);
                            }
                            break;
                        }
                    case OperationContext.AllFiles:
                        {
                            var uniqueWordId = viewModel.WordsListViewModel.LastSelectedWord.UniqueWordId;
                            var posTypeId = viewModel.selectedPOSType.DataModel.RowId;
                            POSTaggerOperations.AddPosTypeToWordInAllFiles(posTypeId, uniqueWordId);
                            var words = viewModel.WordsListViewModel
                                                .AvailableWords
                                                .Where(x => x.UniqueWordId == uniqueWordId)
                                                .Cast<POSTaggerWordViewModel>();
                            foreach (var word in words)
                            {
                                word.DataModel.POSTypes.Add(viewModel.selectedPOSType.DataModel);
                            }
                            break;
                        }
                }
                viewModel.ShowPOSType(viewModel.WordsListViewModel.LastSelectedWord as POSTaggerWordViewModel);
                RaiseCanExecuteChanged();
            }

            public event EventHandler CanExecuteChanged;
        }

        #endregion SetPOSTypeBaseCommand

        #region ClearPOSTypeCommand

        readonly _UnSetPOSTypeCommand unSetPOSTypeCommand;
        public ICommand UnSetPOSTypeCommand
        {
            get { return unSetPOSTypeCommand; }
        }

        private class _UnSetPOSTypeCommand : ICommand
        {
            private POSTaggerViewModel viewModel;
            public _UnSetPOSTypeCommand(POSTaggerViewModel viewModel)
            {
                this.viewModel = viewModel;
                viewModel.PropertyChanged += viewModel_PropertyChanged;
                viewModel.WordsListViewModel.SelectedWords.CollectionChanged += SelectedWords_CollectionChanged;
                viewModel.WordsListViewModel.CurrentWords.SubscribeItemPropChange("IsSet", CurrentWords_IsSetChanged);
            }

            void viewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                if (e.PropertyName == "SelectedAssignedPOSType")
                {
                    RaiseCanExecuteChanged();
                }
            }

            void SelectedWords_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                RaiseCanExecuteChanged();
            }

            void CurrentWords_IsSetChanged(WordViewModelBase model)
            {
                RaiseCanExecuteChanged();
            }

            void RaiseCanExecuteChanged()
            {
                this.CanExecuteChanged(null, EventArgs.Empty);
            }

            public virtual bool CanExecute(object parameter)
            {
                return viewModel.WordsListViewModel.SelectedWords.Any(x => x.IsSet) && viewModel.SelectedAssignedPOSType != null;
            }

            public virtual void Execute(object parameter)
            {
                switch (viewModel.OperationContext)
                {
                    case OperationContext.Selection:
                        POSTaggerOperations.RemovePosTypeFromWords(viewModel.SelectedPOSType.DataModel,
                                viewModel.WordsListViewModel.SelectedWords.Select(x => x.DataModel));
                        viewModel.ShowPOSType(viewModel.WordsListViewModel.LastSelectedWord as POSTaggerWordViewModel);
                        break;
                    case OperationContext.CurrentFile:
                        {
                            var uniqueWordId = viewModel.WordsListViewModel.LastSelectedWord.UniqueWordId;
                            var fileId = viewModel.FileSelectViewModel.SelectedFile.RowId;
                            var posTypeId = viewModel.SelectedAssignedPOSType.DataModel.RowId;
                            POSTaggerOperations.RemovePosTypeFromWordInFile(posTypeId, uniqueWordId, fileId);
                            var words = viewModel.WordsListViewModel
                                                .AvailableWords
                                                .Where(x => x.UniqueWordId == uniqueWordId)
                                                .Cast<POSTaggerWordViewModel>();
                            foreach (var word in words)
                            {
                                word.DataModel.POSTypes.Remove(viewModel.SelectedAssignedPOSType.DataModel);
                            }
                            break;
                        }
                    case OperationContext.AllFiles:
                        {
                            var uniqueWordId = viewModel.WordsListViewModel.LastSelectedWord.UniqueWordId;
                            var posTypeId = viewModel.SelectedAssignedPOSType.DataModel.RowId;
                            POSTaggerOperations.RemovePosTypeFromWordInAllFiles(posTypeId, uniqueWordId);
                            var words = viewModel.WordsListViewModel
                                                .AvailableWords
                                                .Where(x => x.UniqueWordId == uniqueWordId)
                                                .Cast<POSTaggerWordViewModel>();
                            foreach (var word in words)
                            {
                                word.DataModel.POSTypes.Remove(viewModel.SelectedAssignedPOSType.DataModel);
                            }
                            break;
                        }
                }
                RaiseCanExecuteChanged();
            }

            public virtual event EventHandler CanExecuteChanged = (sender, args) => { };
        }

        #endregion SetNumericalCommand

        #endregion

        #region Actions

        public override async Task LoadFile(ProjectFileViewModel fileViewModel)
        {
            HasSelectedFile = false;
            CurrentBaseForm = null;
            AssignedPOSTypes.Clear();
            WordsListViewModel.Unload();
            SentenceViewModel.Unload();

            if (fileViewModel != null)
            {
                var words = await LemmatizerOperations.GetWordsAsync(fileViewModel.RowId);
                var wordViewModels = words.Select(x => new POSTaggerWordViewModel(x))
                                            .Cast<WordViewModelBase>()
                                            .OrderBy(x => x.Content);
                WordsListViewModel.ShowWords(wordViewModels);
                HasSelectedFile = true;
            }
        }

        public void ShowBaseForm(string baseForm)
        {
            CurrentBaseForm = baseForm;
        }

        public void ShowPOSType(POSTaggerWordViewModel wordViewModel)
        {
            AssignedPOSTypes.Clear();
            var posTypeViewModels = wordViewModel.DataModel.POSTypes.Select(x => new FlattenPOSTypeViewModel(x, 0));
            AssignedPOSTypes.AddRange(posTypeViewModels);
        }

        private List<FlattenPOSTypeViewModel> flatten(IEnumerable<POSTypeModel> items, int level)
        {
            var result = new List<FlattenPOSTypeViewModel>();
            foreach (var item in items)
            {
                var itemViewModel = new FlattenPOSTypeViewModel(item, level);
                result.Add(itemViewModel);
                if (item.Children.Count > 0)
                {
                    itemViewModel.IsEnabled = false;
                    result.AddRange(flatten(item.Children, level + 1));
                }
            }
            return result;
        }

        #endregion Actions
    }
}
